#include "udk_os.h"
#include "commapi.h"
#include "crc16.h"
#include "ActiveSocket.h"


typedef struct _HH
{
	Mutex* mtx;
	UDK_BOOL bTerminalRead;
	CActiveSocket* mysocket;
	UDK_S32 SendBufferLen;
	UDK_U8 *SendBuffer;
	UDK_S32 RecvBufferLen;
	UDK_U8 *RecvBuffer;
	UDK_U16 quirks;
}	HH, *PH;

static inline UDK_BOOL get_terminal_cond(HH* h){
  AutoMutex autolock(h->mtx);
  return h->bTerminalRead;
}
static inline UDK_BOOL set_terminal_cond(HH* h,UDK_BOOL v){
  AutoMutex autolock(h->mtx);
  h->bTerminalRead=v;
  return h->bTerminalRead;
}

#ifdef COMM_MODULE_AUTOINSTALL
UDK_HANDLE udpcomm_INIT(PST_COMM_PARAMS param,const char *IPAddress, unsigned short PortNum, int speed)
#else
UDK_HANDLE EMBUDP_INIT(PST_COMM_PARAMS param,const char *IPAddress, unsigned short PortNum, int speed)
#endif
{
	HH	*h = (PH)udk_malloc(sizeof(HH));if(!h) return NULL;
	memset(h,0,sizeof(HH));
	h->mysocket = new CActiveSocket(CSimpleSocket::SocketTypeUdp);
	h->mysocket->Initialize();
	h->mysocket->SetReceiveWindowSize(256*1024);
	
	bool connectok = h->mysocket->Open((const UDK_U8*)IPAddress,PortNum);
	if(false==connectok){
		h->mysocket->Close();
		delete h->mysocket;
		udk_free(h);
		h = NULL;
	}
	if(h){
	    h->mtx=new Mutex();
	    h->SendBufferLen = param->PackageSize + 16;
	    h->SendBuffer = (UDK_U8 *)udk_malloc(h->SendBufferLen);
	    h->RecvBufferLen = param->PackageSize + 16;
	    h->RecvBuffer = (UDK_U8 *)udk_malloc(h->RecvBufferLen);
	    h->quirks = param->Quirks;
	}
	return (UDK_HANDLE)h;
}

#ifdef COMM_MODULE_AUTOINSTALL
UDK_BOOL udpcomm_CLOSE(UDK_HANDLE Handle)
#else
UDK_BOOL EMBUDP_CLOSE(UDK_HANDLE Handle)
#endif
{
	HH	*h=(HH *)Handle;
	if(h){
	    h->mysocket->Close();
	    udk_free(h->SendBuffer);
	    udk_free(h->RecvBuffer);
 	    delete h->mysocket;
	    delete h->mtx;
	    udk_free(h);
	}
	return UDK_TRUE;
}


#ifdef COMM_MODULE_AUTOINSTALL
int udpcomm_SEND(UDK_HANDLE Handle, const char *Data, int Count)
#else
int EMBUDP_SEND(UDK_HANDLE Handle,  const char *Data, int Count)
#endif
{
	HH	*h=(HH *)Handle;	
	int sentLen;
	sentLen = h->mysocket->Send((const unsigned char *)Data, Count);
	if(sentLen>0&&(h->quirks&COMM_PARAM_QUIRK_DEBUG)){
		UDK_DUMP((const char*)("command packet ---->"),(const char*)Data,Count);
        }
	return sentLen;
}
static int CheckReadable(HH *h, int TimeOutMS){
	if(h->mysocket->Select(TimeOutMS/1000,(TimeOutMS%1000)*1000,0x1))
		return 1;
	return 0;
}
#ifdef COMM_MODULE_AUTOINSTALL
int udpcomm_READ(UDK_HANDLE Handle,  char *Data, int Count, int TimeOutMS)
#else
int EMBUDP_READ(UDK_HANDLE Handle,  char *Data, int Count, int TimeOutMS)
#endif
{
	HH	*h=(HH *)Handle;
	static int iIdleReadSign=0;
	int ret, len;
	len=0;	
	UDK_DWORD msStartTime=0, msCurrentTime=0;
	msStartTime=udk_get_ticks();
	set_terminal_cond(h,UDK_FALSE);
	while(1)
	{
		msCurrentTime = udk_get_ticks();
		if((msCurrentTime -msStartTime)>TimeOutMS) break;	
		
		if(UDK_TRUE==get_terminal_cond(h)){
			break;
		}

		if(CheckReadable(h,100)) 
		{
		  len=h->mysocket->Receive(Count);
		  if(len>0){
		    memcpy(Data, h->mysocket->GetData(),h->mysocket->GetBytesReceived());
		    if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
		      UDK_DUMP((const char*)("response packet <----"),(const char*)Data,len);
		    }
		    if(*((unsigned short *)Data) == 500 && strcmp((const char*)h->mysocket->GetClientAddr(),(const char*)h->mysocket->GetServerAddr()))
		    {
		      UDK_LOGW("UDP Buggy event\n");
		      memset(Data, 0, len);
		      continue;
		    }
			  //add end*/
		     break;
		   }
		}
	}
	return len;
}

#ifdef COMM_MODULE_AUTOINSTALL
int udpcomm_SENDCMD(UDK_HANDLE Handle,int Command, char *Data, int Count, PSessionHeader pshd)
#else
int EMBUDP_SENDCMD(UDK_HANDLE Handle,int Command, char *Data, int Count, PSessionHeader pshd)
#endif
{
	HH	*h=(HH *)Handle;
	PProtoHeader pchd = (PProtoHeader)(h->SendBuffer);
	UDK_S32 sendLen = sizeof(TProtoHeader) + CRC_AND_ENDTAG_LEN + Count;
	UDK_S32 sentLen = 0;
	UDK_U8 *p;
	if(Count > h->SendBufferLen-PROTOCOL_AND_SESSION_LEN){
		return ERR_SEND_PACKAGE_BUF_INSUFFICIENT;
	}
	
	p = h->SendBuffer;
	//fill protocol header
	pchd->StartTag = PROTO_START_TAG;
	pchd->DestAddr = 0x1;
	pchd->Command = Command&0xff;
	pchd->DataSize = Count;
	//fill session header
	if(pshd){		
		memcpy(pchd+1,pshd,sizeof(TSessionHeader));		
		sendLen += sizeof(TSessionHeader);
		pchd->DataSize += sizeof(TSessionHeader);
		if(Count) memcpy((char *)(p+sizeof(TProtoHeader)+sizeof(TSessionHeader)), Data, Count);		
	}else{
		if(Count) memcpy((char*)(p+sizeof(TProtoHeader)),Data,Count);
	}

	SETWORD(p+sizeof(TProtoHeader) + pchd->DataSize, crc16(p+1, sendLen -sizeof(TSessionHeader)));
	*(p+sendLen-PROTOCOL_ENDTAG_LEN) = PROTO_END_TAG;
	sentLen = h->mysocket->Send((const UDK_U8*)(h->SendBuffer), sendLen);
	if(sentLen>0&&(h->quirks&COMM_PARAM_QUIRK_DEBUG)){
		UDK_DUMP((const char*)("command packet ---->"),(const char*)(h->SendBuffer),sentLen);
	}
	return sentLen;
}

#ifdef COMM_MODULE_AUTOINSTALL
int udpcomm_READCMD(UDK_HANDLE Handle,int *Command,  char *Data, int *Count, int TimeOutMS)
#else
int EMBUDP_READCMD(UDK_HANDLE Handle,int *Command,  char *Data, int *Count, int TimeOutMS)
#endif
{
	HH	*h=(HH *)Handle;
	PProtoHeader pchd = (PProtoHeader)h->RecvBuffer;
	char *p = (char*)h->RecvBuffer;
	int dataBufInLen = *Count;
	int len=0;	
	UDK_DWORD msStartTime=0, msCurrentTime=0;
	
	msStartTime=udk_get_ticks();
	set_terminal_cond(h,UDK_FALSE);
	while(1)
	{
		msCurrentTime = udk_get_ticks();
		if((msCurrentTime-msStartTime) > TimeOutMS){
			break;
		}
		//for round
		if(msCurrentTime < msStartTime)	{
			break;
		}
		if(UDK_TRUE==get_terminal_cond(h)) 
			break;

		if(CheckReadable(h, 100)) {
			len=h->mysocket->Receive(h->RecvBufferLen);		
			UDK_WORD crc16r ;
			UDK_WORD crc16l ;
			if(len>=(int)(sizeof(TProtoHeader)+3))	
			{
				memcpy(h->RecvBuffer,h->mysocket->GetData(),len);
				if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
					UDK_DUMP((const char*)("response packet <----"),(const char*)(h->RecvBuffer),len);		
				}
				crc16r = GETWORD(p+len -3);
				crc16l = crc16((UDK_U8*)(p+1),len -4);
				if(crc16r == crc16l){
						*Command = pchd->Command;
				if(pchd->DataSize <*Count)
						*Count = pchd->DataSize;
						memcpy(Data,pchd+1,pchd->DataSize);
						*Count = pchd->DataSize;
				}else
					len = -1;
			}
			
			break;
			
	    }//if checkable)
	    udk_msleep(10);	
	} //while 1

	return len;
}

#ifdef COMM_MODULE_AUTOINSTALL
int udpcomm_PREREAD(UDK_HANDLE Handle)
#else
int EMBUDP_PREREAD(UDK_HANDLE Handle)
#endif
{	
	#if COMM_MODULE_AUTOINSTALL
	return udpcomm_READ(Handle, NULL, 0, 0);
	#else
	return EMBUDP_READ(Handle, NULL, 0, 0);
	#endif
}
#ifdef COMM_MODULE_AUTOINSTALL
int udpcomm_TERMINALREAD(UDK_HANDLE Handle)
#else
int EMBUDP_TERMINALREAD(UDK_HANDLE Handle)
#endif
{
	HH	*h=(HH *)Handle;
	return set_terminal_cond(h,UDK_TRUE);
}

#ifdef COMM_MODULE_AUTOINSTALL
const char * udpcomm_NAME(UDK_HANDLE Handle)
#else
const char * EMBUDP_NAME(UDK_HANDLE Handle)
#endif
{
	return (const char*)"UDP";
}

#ifdef COMM_MODULE_AUTOINSTALL

class CCommModuleUdp{
	public:

		CCommModuleUdp(){
			selfInstall();
		}
		virtual ~CCommModuleUdp(){
		  uninstall_comm_module((const char*)"UDP");
		}
		void selfInstall(void){			
			ST_COMM_MODULE mycomm={
					"UDP",
					"udpcomm",
					udpcomm_INIT,
					udpcomm_CLOSE,
					udpcomm_SEND,
					udpcomm_READ,
					udpcomm_TERMINALREAD,
					udpcomm_NAME,
					NULL,//flush
					NULL,//at->modem
					NULL,//at<-modem		
					NULL,//error
					udpcomm_SENDCMD,//sendcmd
					udpcomm_READCMD,//readcmd
			};
			install_comm_module(&mycomm);
		}

};
static CCommModuleUdp comminstance;
#endif

